#ifndef REDIS_CLIENT_H
#define REDIS_CLIENT_H


#include <string>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <string.h>
#include "hiredis.h"
#include "log.h"

#define RC_RESULT_EOF       5
#define RC_NO_EFFECT        4
#define RC_OBJ_NOT_EXIST    3
#define RC_OBJ_EXIST        2
#define RC_PART_SUCCESS     1
#define RC_SUCCESS          0
#define RC_PARAM_ERR        -1
#define RC_REPLY_ERR        -2
#define RC_RQST_ERR         -3
#define RC_NO_RESOURCE      -4
#define RC_PIPELINE_ERR     -5
#define RC_NOT_SUPPORT      -6
#define RC_SLOT_CHANGED     -100

#define RQST_RETRY_TIMES    3
#define WAIT_RETRY_TIMES    60

//#define FUNC_DEF_CONV       [](int nRet, redisReply *) { return nRet; }

//typedef std::function<int (redisReply *)> TFuncFetch;
//typedef std::function<int (int, redisReply *)> TFuncConvert;
typedef  int (*TFuncConvert)(int, redisReply *);
int FUNC_DEF_CONV(int nRet, redisReply *);

class CRedisCommand
{
public:
    CRedisCommand(const std::string &strCmd, bool bShareMem = false);
    virtual ~CRedisCommand() { ClearArgs(); }
    void ClearArgs();
    void DumpArgs() const;
    void DumpReply() const;

    int GetSlot() const { return m_nSlot; }
    const redisReply * GetReply() const { return m_pReply; }
    std::string FetchErrMsg() const;
    bool IsMovedErr() const;

    void SetSlot(int nSlot) { m_nSlot = nSlot; }
    void SetConvFunc(TFuncConvert funcConv) { m_funcConv = funcConv; }

    void SetArgs();
    void SetArgs(const std::string &strArg);
    void SetArgs(const std::vector<std::string> &vecArg);
    void SetArgs(const std::string &strArg1, const std::string &strArg2);
    void SetArgs(const std::string &strArg1, const std::vector<std::string> &vecArg2);
    void SetArgs(const std::string &strArg1, const std::set<std::string> &setArg2);
    void SetArgs(const std::vector<std::string> &vecArg1, const std::string &strArg2);
    void SetArgs(const std::vector<std::string> &vecArg1, const std::vector<std::string> &vecArg2);
    void SetArgs(const std::map<std::string, std::string> &mapArg);
    void SetArgs(const std::string &strArg1, const std::map<std::string, std::string> &mapArg2);
    void SetArgs(const std::string &strArg1, const std::string &strArg2, const std::string &strArg3);
    void SetArgs(const std::string &strArg1, const std::string &strArg2, const std::vector<std::string> &vecArg2);
    void SetArgs(const std::string &strArg1, const std::vector<std::string> &vecArg2, const std::vector<std::string> &vecArg3);
    void SetArgs(const std::string &strArg1, const std::string &strArg2, const std::string &strArg3, const std::string &strArg4);

    int CmdRequest(redisContext *pContext);
    int CmdAppend(redisContext *pContext);
	int CmdReply(redisContext *pContext);
	//int FetchResult(const TFuncFetch &funcFetch)
	//{
	//	return m_funcConv(funcFetch(m_pReply), m_pReply);
	//}

	template<typename TFuncFetch, typename TFuncVal>
	int FetchResult(const TFuncFetch  funcFetch, TFuncVal funVal){
		return m_funcConv(funcFetch(m_pReply, funVal), m_pReply);
	}
private:
    void InitMemory(int nArgs);
    void AppendValue(const std::string &strVal);

protected:
    std::string m_strCmd;
	std::string m_strFormat;
    bool m_bShareMem;

    int m_nArgs;
    int m_nIdx;
    char **m_pszArgs;
    size_t *m_pnArgsLen;
    redisReply *m_pReply;

    int m_nSlot;
    TFuncConvert m_funcConv;
};

class CRedisServer;
class CRedisConnection
{
public:
    CRedisConnection(CRedisServer *pRedisServ);
    ~CRedisConnection();
    bool IsValid() { return m_pContext != NULL; }
    bool ConnectToRedis();
    bool Reconnect();

	int ConnRequest(CRedisCommand *pRedisCmd);
private:
    redisContext *m_pContext;
    time_t m_nUseTime;
    CRedisServer *m_pRedisServ;
};

class CRedisServer
{
public:
	CRedisServer();
   // CRedisServer(const std::string &strHost, int nPort, int nTimeout);
    virtual ~CRedisServer();
	bool InitRedisConnect(const std::string &strHost, int nPort, int nTimeout, std::string& strAuthPwd);
	

    std::string GetHost() const { return m_strHost; }
    int GetPort() const { return m_nPort; }

    // for the blocking request
    int ServRequest(CRedisCommand *pRedisCmd);

    // for the pipeline requirement
    //int ServRequest(std::vector<CRedisCommand *> &vecRedisCmd);

public:
    std::string m_strHost;
    int m_nPort;
	std::string m_strAuthPassword;
    int m_nCliTimeout;
    int m_nSerTimeout;
	CRedisConnection  m_Conn;  
};


typedef void * Pipeline;
class RedisClient
{

public:
    RedisClient();
    ~RedisClient();

	bool InitRedisConnect(std::string strHost, int nPort, std::string strAuthPwd="");
	bool IsValid();


    /* interfaces for generic */
    int Del(const std::string &strKey, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Mdel(const std::vector<std::string> &vecKeys, long *pnVal = NULL, Pipeline ppLine = NULL);//批量删除键
    int Dump(const std::string &strKey, std::string *pstrVal, Pipeline ppLine = NULL);
    int Exists(const std::string &strKey, long *pnVal, Pipeline ppLine = NULL);
    int Expire(const std::string &strKey, long nSec, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Expireat(const std::string &strKey, long nTime, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Keys(const std::string &strPattern, std::vector<std::string> *pvecVal);
    int Persist(const std::string &strKey, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Pexpire(const std::string &strKey, long nMilliSec, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Pexpireat(const std::string &strKey, long nMilliTime, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Pttl(const std::string &strKey, long *pnVal, Pipeline ppLine = NULL);
    int Randomkey(std::string *pstrVal, Pipeline ppLine = NULL);
    int Rename(const std::string &strKey, const std::string &strNewKey);
    int Renamenx(const std::string &strKey, const std::string &strNewKey);
    int Restore(const std::string &strKey, long nTtl, const std::string &strVal, Pipeline ppLine = NULL);
    int Scan(long *pnCursor, const std::string &strPattern, long nCount, std::vector<std::string> *pvecVal);
    int Ttl(const std::string &strKey, long *pnVal, Pipeline ppLine = NULL);
    int Type(const std::string &strKey, std::string *pstrVal, Pipeline ppLine = NULL);

    /* interfaces for string */
    int Append(const std::string &strKey, const std::string &strVal, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Bitcount(const std::string &strKey, long *pnVal, Pipeline ppLine = NULL);
    int Bitcount(const std::string &strKey, long nStart, long nEnd, long *pnVal, Pipeline ppLine = NULL);
    int Bitop(const std::string &strDestKey, const std::string &strOp, const std::vector<std::string> &vecKey, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Bitpos(const std::string &strKey, long nBitVal, long *pnVal, Pipeline ppLine = NULL);
    int Bitpos(const std::string &strKey, long nBitVal, long nStart, long nEnd, long *pnVal, Pipeline ppLine = NULL);
    int Decr(const std::string &strKey, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Decrby(const std::string &strKey, long nDecr, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Get(const std::string &strKey, std::string *pstrVal, Pipeline ppLine = NULL);
    int Getbit(const std::string &strKey, long nOffset, long *pnVal, Pipeline ppLine = NULL);
    int Getrange(const std::string &strKey, long nStart, long nEnd, std::string *pstrVal, Pipeline ppLine = NULL);
    int Getset(const std::string &strKey, std::string *pstrVal, Pipeline ppLine = NULL);
    int Incr(const std::string &strKey, long *pnVal, Pipeline ppLine = NULL);
    int Incrby(const std::string &strKey, long nIncr, long *pnVal, Pipeline ppLine = NULL);
    int Incrbyfloat(const std::string &strKey, double dIncr, double *pdVal, Pipeline ppLine = NULL);
    int Mget(const std::vector<std::string> &vecKey, std::vector<std::string> *pvecVal);
    int Mset(const std::vector<std::string> &vecKey, const std::vector<std::string> &vecVal);
    int Psetex(const std::string &strKey, long nMilliSec, const std::string &strVal, Pipeline ppLine = NULL);
    int Set(const std::string &strKey, const std::string &strVal, Pipeline ppLine = NULL);
    int Setbit(const std::string &strKey, long nOffset, bool bVal, Pipeline ppLine = NULL);
    int Setex(const std::string &strKey, long nSec, const std::string &strVal, Pipeline ppLine = NULL);
    int Setnx(const std::string &strKey, const std::string &strVal, Pipeline ppLine = NULL);
    int Setrange(const std::string &strKey, long nOffset, const std::string &strVal, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Strlen(const std::string &strKey, long *pnVal, Pipeline ppLine = NULL);

    /* interfaces for list */
    int Blpop(const std::string &strKey, long nTimeout, std::vector<std::string> *pvecVal);
    int Blpop(const std::vector<std::string> &vecKey, long nTimeout, std::vector<std::string> *pvecVal);
    int Brpop(const std::string &strKey, long nTimeout, std::vector<std::string> *pvecVal);
    int Brpop(const std::vector<std::string> &vecKey, long nTimeout, std::vector<std::string> *pvecVal);
    int Lindex(const std::string &strKey, long nIndex, std::string *pstrVal, Pipeline ppLine = NULL);
    int Linsert(const std::string &strKey, const std::string &strPos, const std::string &strPivot, const std::string &strVal, long *pnVal, Pipeline ppLine = NULL);
    int Llen(const std::string &strKey, long *pnVal, Pipeline ppLine = NULL);
    int Lpop(const std::string &strKey, std::string *pstrVal, Pipeline ppLine = NULL);
    int Lpush(const std::string &strKey, const std::string &strVal, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Lpush(const std::string &strKey, const std::vector<std::string> &vecVal, Pipeline ppLine = NULL);
    int Lpushx(const std::string &strKey, const std::string &strVal, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Lrange(const std::string &strKey, long nStart, long nStop, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Lrem(const std::string &strKey, long nCount, const std::string &strVal, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Lset(const std::string &strKey, long nIndex, const std::string &strVal, Pipeline ppLine = NULL);
    int Ltrim(const std::string &strKey, long nStart, long nStop, Pipeline ppLine = NULL);
    int Rpop(const std::string &strKey, std::string *pstrVal, Pipeline ppLine = NULL);
    int Rpush(const std::string &strKey, const std::string &strVal, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Rpush(const std::string &strKey, const std::vector<std::string> &vecVal, Pipeline ppLine = NULL);
    int Rpushx(const std::string &strKey, const std::string &strVal, long *pnVal = NULL, Pipeline ppLine = NULL);

    /* interfaces for set */
    int Sadd(const std::string &strKey, const std::string &strVal, long *pnVal = NULL, Pipeline = NULL);
    int Scard(const std::string &strKey, long *pnVal, Pipeline = NULL);
    //int Sdiff(const std::vector<std::string> &vecKey, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    //int Sinter(const std::vector<std::string> &vecKey, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Sismember(const std::string &strKey, const std::string &strVal, long *pnVal, Pipeline ppLine = NULL);
    int Smembers(const std::string &strKey, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Spop(const std::string &strKey, std::string *pstrVal, Pipeline ppLine = NULL);
    //int Srandmember(const std::string &strKey, long nCount, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Srandmember(const std::string &strKey, std::string *pstrVal, Pipeline ppLine = NULL);
    int Srem(const std::string &strKey, const std::string &strVal, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Srem(const std::string &strKey, const std::vector<std::string> &vecVal, long *pnVal = NULL, Pipeline ppLine = NULL);
    //int Sunion(const std::vector<std::string> &vecKey, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);

    /* interfaces for hash */
    int Hdel(const std::string &strKey, const std::string &strField, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Hexists(const std::string &strKey, const std::string &strField, long *pnVal, Pipeline ppLine = NULL);
    int Hget(const std::string &strKey, const std::string &strField, std::string *pstrVal, Pipeline ppLine = NULL);
    int Hgetall(const std::string &strKey, std::map<std::string, std::string> *pmapFv, Pipeline ppLine = NULL);
    int Hincrby(const std::string &strKey, const std::string &strField, long nIncr, long *pnVal, Pipeline ppLine = NULL);
    int Hincrbyfloat(const std::string &strKey, const std::string &strField, double dIncr, double *pdVal, Pipeline ppLine = NULL);
    int Hkeys(const std::string &strKey, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Hlen(const std::string &strKey, long *pnVal, Pipeline ppLine = NULL);
    int Hmget(const std::string &strKey, const std::vector<std::string> &vecField, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Hmget(const std::string &strKey, const std::vector<std::string> &vecField, std::map<std::string, std::string> *pmapVal);
    int Hmget(const std::string &strKey, const std::set<std::string> &setField, std::map<std::string, std::string> *pmapVal);
    int Hmset(const std::string &strKey, const std::vector<std::string> &vecField, const std::vector<std::string> &vecVal, Pipeline ppLine = NULL);
    int Hmset(const std::string &strKey, const std::map<std::string, std::string> &mapFv, Pipeline ppLine = NULL);
    //int Hscan(const std::string &strKey, long *pnCursor, const std::string &strMatch, long nCount, std::vector<std::string> *pvecVal);
    int Hset(const std::string &strKey, const std::string &strField, const std::string &strVal, Pipeline ppLine = NULL);
    int Hsetnx(const std::string &strKey, const std::string &strField, const std::string &strVal, Pipeline ppLine = NULL);
    int Hvals(const std::string &strKey, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);

    /* interfaces for sorted set */
    int Zadd(const std::string &strKey, double dScore, const std::string &strElem, long *pnVal = NULL, Pipeline = NULL);
    int Zcard(const std::string &strKey, long *pnVal, Pipeline = NULL);
    int Zcount(const std::string &strKey, double dMin, double dMax, long *pnVal, Pipeline ppLine = NULL);
    int Zincrby(const std::string &strKey, double dIncr, const std::string &strElem, double *pdVal, Pipeline ppLine = NULL);
    int Zlexcount(const std::string &strKey, const std::string &strMin, const std::string &strMax, long *pnVal, Pipeline ppLine = NULL);
    int Zrange(const std::string &strKey, long nStart, long nStop, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Zrangewithscore(const std::string &strKey, long nStart, long nStop, std::map<std::string, std::string> *pmapVal, Pipeline ppLine = NULL);
    int Zrangebylex(const std::string &strKey, const std::string &strMin, const std::string &strMax, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Zrangebyscore(const std::string &strKey, double dMin, double dMax, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Zrangebyscore(const std::string &strKey, double dMin, double dMax, std::map<std::string, double> *pmapVal, Pipeline ppLine = NULL);
    int Zrank(const std::string &strKey, const std::string &strElem, long *pnVal, Pipeline ppLine = NULL);
    int Zrem(const std::string &strKey, const std::string &strElem, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Zrem(const std::string &strKey, const std::vector<std::string> &vecElem, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Zremrangebylex(const std::string &strKey, const std::string &strMin, const std::string &strMax, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Zremrangebyrank(const std::string &strKey, long nStart, long nStop, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Zremrangebyscore(const std::string &strKey, double dMin, double dMax, long *pnVal = NULL, Pipeline ppLine = NULL);
    int Zrevrange(const std::string &strKey, long nStart, long nStop, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Zrevrangewithscore(const std::string &strKey, long nStart, long nStop, std::map<std::string, std::string> *pmapVal, Pipeline ppLine = NULL);
    int Zrevrangebyscore(const std::string &strKey, double dMax, double dMin, std::vector<std::string> *pvecVal, Pipeline ppLine = NULL);
    int Zrevrangebyscore(const std::string &strKey, double dMax, double dMin, std::map<std::string, double> *pmapVal, Pipeline ppLine = NULL);
    int Zrevrank(const std::string &strKey, const std::string &strElem, long *pnVal, Pipeline ppLine = NULL);
    int Zscore(const std::string &strKey, const std::string &strElem, double *pdVal, Pipeline ppLine = NULL);

    /* interfaces for system */
    int Time(struct timeval *ptmVal, Pipeline ppLine = NULL);

private:
    static bool ConvertToMapInfo(const std::string &strVal, std::map<std::string, std::string> &mapVal);


    int Execute(CRedisCommand *pRedisCmd);
    int SimpleExecute(CRedisCommand *pRedisCmd);

	template <typename TFuncFetch, typename TFuncVal>
    int ExecuteImpl(const std::string &strCmd, int nSlot, Pipeline ppLine,
			TFuncFetch funcFetch, TFuncVal funVal, TFuncConvert funcConv = FUNC_DEF_CONV)
	{
		CRedisCommand redisCmd(strCmd);

		redisCmd.SetArgs();
		redisCmd.SetSlot(nSlot);
		redisCmd.SetConvFunc(funcConv);
		int nRet = Execute(&redisCmd);
		if (nRet == RC_SUCCESS)
			nRet = redisCmd.FetchResult(funcFetch, funVal);

		return nRet;
	}

	template <typename TFuncFetch, typename TFuncVal, typename P>
    int ExecuteImpl(const std::string &strCmd, const P &tArg, int nSlot, Pipeline ppLine,
		TFuncFetch funcFetch, TFuncVal funVal, TFuncConvert funcConv = FUNC_DEF_CONV)
    {
		CRedisCommand redisCmd(strCmd);
		redisCmd.SetArgs(tArg);
		redisCmd.SetSlot(nSlot);
		redisCmd.SetConvFunc(funcConv);
		int nRet = Execute(&redisCmd);
        if (nRet == RC_SUCCESS)
			nRet = redisCmd.FetchResult(funcFetch, funVal);
        
        return nRet;
    }

	template <typename TFuncFetch, typename TFuncVal, typename P1, typename P2>
    int ExecuteImpl(const std::string &strCmd, const P1 &tArg1, const P2 &tArg2, int nSlot, Pipeline ppLine,
		TFuncFetch funcFetch, TFuncVal funVal, TFuncConvert funcConv = FUNC_DEF_CONV)
    {
		CRedisCommand redisCmd(strCmd);
		redisCmd.SetArgs(tArg1, tArg2);
		redisCmd.SetSlot(nSlot);
		redisCmd.SetConvFunc(funcConv);
		int nRet = Execute(&redisCmd);
        if (nRet == RC_SUCCESS)
			nRet = redisCmd.FetchResult(funcFetch, funVal);

    
        return nRet;
    }

	template <typename TFuncFetch, typename TFuncVal, typename P1, typename P2, typename P3>
    int ExecuteImpl(const std::string &strCmd, const P1 &tArg1, const P2 &tArg2, const P3 &tArg3, int nSlot, Pipeline ppLine,
			TFuncFetch funcFetch, TFuncVal funVal, TFuncConvert funcConv = FUNC_DEF_CONV)
    {
		CRedisCommand redisCmd(strCmd);
		redisCmd.SetArgs(tArg1, tArg2, tArg3);
		redisCmd.SetSlot(nSlot);
		redisCmd.SetConvFunc(funcConv);
		int nRet = Execute(&redisCmd);
        if (nRet == RC_SUCCESS)
			nRet = redisCmd.FetchResult(funcFetch, funVal);
        

        return nRet;
    }

	template <typename TFuncFetch, typename TFuncVal, typename P1, typename P2, typename P3, typename P4>
    int ExecuteImpl(const std::string &strCmd, const P1 &tArg1, const P2 &tArg2, const P3 &tArg3, const P4 &tArg4, int nSlot, Pipeline ppLine,
			TFuncFetch funcFetch, TFuncVal funVal, TFuncConvert funcConv = FUNC_DEF_CONV)
    {
		CRedisCommand redisCmd(strCmd);
		redisCmd.SetArgs(tArg1, tArg2, tArg3, tArg4);
		redisCmd.SetSlot(nSlot);
		redisCmd.SetConvFunc(funcConv);
		int nRet = Execute(&redisCmd);
        if (nRet == RC_SUCCESS)
			nRet = redisCmd.FetchResult(funcFetch, funVal);
        
        return nRet;
    }

public:
	std::string m_strHost;
    int m_nPort;
    int m_nTimeout;
	CRedisServer  m_RedisServer;
};

#endif
